#!/usr/bin/env python
# -*- coding: utf-8 -*- 
'''
'''

from lxml import etree

from pybible import linguals

def _loadXMLDATA(buf):
    return _XMLData(etree.XML(buf.read()))

class _XMLData(object):
    def __init__(self, element, root=None):
        root = root or etree.ElementTree(element)
        self._element = element
        self._root = root

    @property
    def text(self):
        return self._element.text

    @property
    def tag(self):
        "Return the xml element's tag"
        return self._element.tag

    @property
    def attrs(self):
        "Return xml element's attributs."
        return self._element.attrib

    def _build_child(self, element, factory=None):
        "This method are use to build a child. Prefer use it than __init__."
        if not element is None:
            child = self.__class__(element, self._root)
            if factory is not None:
                return factory(child)
            return child

    def maxdeep(self):
        "Return max tree deep."
        def _deep(element):
            "Recursive computation of the tree deep on etree element."
            deep = 1
            if len(element) > 0:
                deep = max([_deep(e) for e in element]) + 1
            return deep
        return _deep(self._element)

    def mydeep(self):
        "Return my deep (number of parent of myself)."
        deep = 0
        element = self._element
        while element is not None:
            element = element.getparent()
            deep += 1
        return deep

    def get_xpath(self):
        "Retrive my xpath into the etree."
        return self._root.getpath(self._element)

    def eval_xpath(self, xpath, relatif=False, factory=None, **kw):
        "Return list of mashing nodes with xpath."

        if relatif:
            return [self._build_child(e, factory)
                    for e in self._element.xpath(xpath, **kw)]
        else:
            return [self._build_child(e, factory)
                    for e in self._root.xpath(xpath, **kw)]

    def get_id(self):
        "Return the index of this element."
        return self.get_parent()._element.index(self._element)

    def get_parent(self):
        "Returns the following sibling of this element or None."
        return self._build_child(self._element.getparent())

    def get_next(self):
        "Returns the following sibling of this element or None."
        return self._build_child(self._element.getnext())

    def get_previous(self):
        "Returns the preceding sibling of this element or None."
        return self._build_child(self._element.getprevious())

    def __len__(self):
        return len(self._element)

    def __iter__(self):
        "Iter of child of this element."
        for item in self._element:
            yield self._build_child(item)

    def __getitem__(self, i):
        return self._build_child(self._element[i])


    def iter(self):
        "Iter of child of this element."
        for item in self._element.iter():
            yield self._build_child(item)

    def __cmp__(self, other):
        return cmp(self._element, other._element)
    def __eq__(self, other):
        return self._element == other._element

def loadBookPart(buf):
    return BookPart(_loadXMLDATA(buf))

class BookPart(object):
    def __init__(self, data):
        self._data = data

    def get_data(self):
        return self._data

    @property
    def part_name_type(self):
        "Return the part name"
        return self._data.tag

    def _build_child(self, data):
        "This method are use to build a child. Prefer use it than __init__."
        return self.__class__(data)

    def get_next(self, flat=False):
        """Return the next book part.
        
        If the element is at the end of the parent. The next part is the next
        parent. It FLAT is set return the first sub-part of the next parent. 
        
        eg::
        
            root
            |-- part_1
            |   |-- sub_part_11
            |   `-- sub_part_12
            `-- part_2
                |-- sub_part_21 
                `-- sub_part_22
                
                
            >>> print sub_part_11.get_next()
            root/part_2
            >>> print sub_part_11.get_next(flat=True)
            root/part_2/sub_part_21
            
        """
        next = self._data.get_next()
        if next is None:
            if flat:
                next = self._data.get_parent()
                if not next is None:
                    next = next.get_next()
                if next is None:
                    return None
                if len(next) > 0:
                    next = next[0]
                else:
                    return None
        if not next is None:
            return self._build_child(next)

    def get_previous(self, flat=False):
        """Return the next book part.
        
        If the element is at the end of the parent. The next part is the next
        parent. It FLAT is set return the first sub-part of the next parent. 
        
        eg::
        
            root
            |-- part_1
            |   |-- sub_part_11
            |   `-- sub_part_12
            `-- part_2
                |-- sub_part_21 
                `-- sub_part_22
                
                
            >>> print sub_part_21.get_previous()
            root/part_1
            >>> print sub_part_21.get_next(flat=True)
            root/part_1/sub_part_12
            
        """
        prev = self._data.get_previous()
        if prev is None:
            if flat:
                prev = self._data.get_parent()
                if not prev is None:
                    prev = prev.get_previous()
                if prev is None:
                    return None
                if len(prev) > 0:
                    prev = prev[-1]
                else:
                    return None
        if not prev is None:
            return self._build_child(prev)

    def get_parent(self):
        parent = self._data.get_parent()
        if not parent is None:
            return self._build_child(parent)




def openBook(file):
    fd = open(file)
    book = loadBook(fd)
    fd.close()
    return book

def loadBook(buf):
    return Book(_loadXMLDATA(buf))

class Book(BookPart):
    def __init__(self, data):
        self._data = data

    def get_part(self):
        return BookPart(self._data.eval_xpath("/book/part")[0])
    def get_header(self):
        return _BookHeader(self._data.eval_xpath('/book/header')[0])

def _loadBookHeader(data):
    return _BookHeader(data)


BOOK_HEADER_ATOMIC = ('title', 'language', 'translation')
BOOK_HEADER_MULTI = ('type', 'topic', 'author')
class _BookHeader(object):

    def __init__(self, data,
                 atomic=BOOK_HEADER_ATOMIC,
                 multi=BOOK_HEADER_MULTI):
        self._data = data
        self._atomic_keys = atomic
        self._multi_keys = multi


    def isAtomicKey(self, key):
        return key in self._atomic_keys
    def isMultiKey(self, key):
        return key in self._multi_keys
    def get_valideKey(self):
        return self._multi_keys + self._atomic_keys

    def __getitem__(self, key):
        xpath = "header/key[@type='%s']" % key
        item = [item.attrs['value']
                for item in self._data.eval_xpath(xpath)]

        if self.isAtomicKey(key):
            if len(item) == 1:
                return item[0]
            elif len(item) != 0:
                raise
            else:
                return None
        else:
            return item


    def get_lingual(self):
        "Return language support if it is."

        language = self['language']
        if (language is not None
            and linguals.has_langual(language)):
            return linguals.get_langual(language)



